Search by

afurgeri / laravel-arca

afurgeri

Electronic invoicing for ARCA in Laravel applications.

Package info

github.com/afurgeri/laravel-arca

pkg:composer/afurgeri/laravel-arca

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.1 2026-09-09 13:41 UTC

This package is auto-updated.

Last update: 2026-09-12 19:46:45 UTC


README

Facturación electrónica para aplicaciones Laravel mediante AfipSDK, actualmente integrado con ARCA.

El paquete mantiene al CRM separado de los códigos y estructuras de ARCA. El CRM trabaja con términos de negocio como Factura A, Nota de crédito B, USD y punto de venta; el módulo realiza el mapeo, administra la numeración, autoriza el comprobante y conserva el intercambio técnico completo.

El módulo no envía ni administra artículos individuales. La autorización recibe importes agrupados y la discriminación de IVA requerida por ARCA.

Requisitos

  • PHP 8.3+
  • Laravel 13+
  • Extensión PHP SOAP
  • Extensión PHP OpenSSL
  • Una cuenta y credenciales de AfipSDK
  • Certificado y clave privada para producción, según la configuración de ARCA/AfipSDK

Instalación

composer require afurgeri/laravel-arca
php artisan migrate

Laravel registra automáticamente el ArcaServiceProvider mediante Composer package discovery.

El paquete carga sus migraciones automáticamente. No es necesario copiarlas a database/migrations.

Configuración

Para publicar el archivo de configuración:

php artisan vendor:publish --tag=arca-config

Esto crea config/arca.php. La configuración predeterminada es:

return [
    'cuit' => env('ARCA_CUIT'),
    'access_token' => env('ARCA_ACCESS_TOKEN'),
    'certificate_path' => env('ARCA_CERTIFICATE_PATH'),
    'private_key_path' => env('ARCA_PRIVATE_KEY_PATH'),
    'production' => (bool) env('ARCA_PRODUCTION', false),
    'table_prefix' => env('ARCA_TABLE_PREFIX', 'arca_'),
    'route_prefix' => env('ARCA_ROUTE_PREFIX', 'api/arca'),
    'route_middleware' => ['web', 'auth', 'verified'],
];

Variables de entorno:

ARCA_CUIT=20111111112
ARCA_ACCESS_TOKEN=your-afipsdk-access-token
ARCA_CERTIFICATE_PATH=/secure/path/certificate.crt
ARCA_PRIVATE_KEY_PATH=/secure/path/private.key
ARCA_PRODUCTION=false
ARCA_TABLE_PREFIX=arca_
ARCA_ROUTE_PREFIX=api/arca

ARCA_PRODUCTION=false utiliza el ambiente de homologación. Cambiarlo a true solamente cuando el certificado, la clave, el CUIT y el access token correspondan a producción.

No guardar certificados, claves privadas ni access tokens en el repositorio o en la base de datos. Los archivos de certificado deben tener permisos restrictivos y no deben quedar dentro de un directorio público.

Después de modificar variables de entorno en una aplicación con configuración cacheada:

php artisan config:clear
php artisan config:cache

Camino rápido

1. Crear un punto de venta

Mediante el servicio PHP:

use Modules\Arca\ArcaManager;

$pointOfSale = app(ArcaManager::class)->pointOfSales()->create([
    'number' => 1,
    'name' => 'Casa central',
    'active' => true,
]);

O mediante HTTP:

POST /api/arca/point-of-sales
Content-Type: application/json
{
    "number": 1,
    "name": "Casa central",
    "active": true
}

2. Autorizar una factura

use Modules\Arca\ArcaManager;

$result = app(ArcaManager::class)->authorize([
    'point_of_sale' => 1,
    'letter' => 'A',
    'operation' => 'invoice',
    'concept' => 'products',
    'customer' => [
        'document_type' => 'cuit',
        'document_number' => '30709999999',
        'vat_condition' => 'responsible_registered',
    ],
    'amounts' => [
        'total' => 121000,
        'taxable_net' => 100000,
        'vat' => 21000,
        'exempt' => 0,
        'non_taxed' => 0,
        'other_taxes' => 0,
    ],
    'vat_rates' => [
        [
            'rate' => 21,
            'taxable_base' => 100000,
            'amount' => 21000,
        ],
    ],
    'idempotency_key' => 'crm-order-4587',
]);

$data = $result->toArray();

El resultado autorizado tiene esta forma:

[
    'success' => true,
    'status' => 'authorized',
    'voucher_id' => 1,
    'point_of_sale' => 1,
    'voucher_number' => 125,
    'formatted_number' => '00001-00000125',
    'currency' => 'ARS',
    'exchange_rate' => 1,
    'cae' => '74012345678901',
    'cae_expires_at' => '2026-09-18',
    'message' => null,
]

El voucher_id es el identificador interno de la aplicación. El CRM no necesita usar códigos internos de ARCA.

API HTTP

Las rutas se cargan automáticamente con el prefijo configurado en arca.route_prefix.

Método Ruta Uso
GET /api/arca/point-of-sales Lista puntos de venta activos
GET /api/arca/point-of-sales/arca Consulta puntos de venta directamente en ARCA
POST /api/arca/point-of-sales/sync Sincroniza puntos de venta y contadores locales con ARCA
POST /api/arca/point-of-sales Crea un punto de venta
POST /api/arca/vouchers/authorize Autoriza un comprobante

Las rutas utilizan por defecto los middlewares web, auth y verified. Para una integración mediante API, publicar config/arca.php y reemplazar route_middleware por los middlewares de autenticación de la aplicación.

Ejemplo de autorización HTTP:

POST /api/arca/vouchers/authorize
Content-Type: application/json
{
    "point_of_sale": 1,
    "letter": "B",
    "operation": "invoice",
    "concept": "products",
    "customer": {
        "document_type": "consumer_final",
        "document_number": 0,
        "vat_condition": "consumer_final"
    },
    "amounts": {
        "total": 1210,
        "taxable_net": 1000,
        "vat": 210,
        "exempt": 0,
        "non_taxed": 0,
        "other_taxes": 0
    },
    "vat_rates": [
        {
            "rate": 21,
            "taxable_base": 1000,
            "amount": 210
        }
    ],
    "idempotency_key": "crm-order-4587"
}

Consultar puntos de venta en ARCA

AfipSDK expone ElectronicBilling->GetSalesPoints(). El módulo consulta ese servicio usando el CUIT configurado en ARCA_CUIT y obtiene también el último número de cada tipo de comprobante.

use Modules\Arca\ArcaManager;

$salesPoints = app(ArcaManager::class)->pointOfSales()->fromArca();

La respuesta usa términos del módulo y no expone códigos de ARCA:

[
    [
        'number' => 1,
        'name' => 'Casa central',
        'active' => true,
        'counters' => [
            [
                'letter' => 'A',
                'operation' => 'invoice',
                'label' => 'Factura A',
                'last_number' => 125,
                'next_number' => 126,
            ],
            // Notas de crédito/débito y comprobantes B/C...
        ],
    ],
]

Para persistir esos puntos y actualizar las secuencias locales:

$salesPoints = app(ArcaManager::class)->pointOfSales()->syncFromArca();

También puede ejecutarse por HTTP:

POST /api/arca/point-of-sales/sync

La sincronización actualiza el nombre y el estado de cada punto de venta, crea las secuencias faltantes y nunca retrocede un contador local que ya haya reservado un número.

En homologación, ARCA normalmente expone sólo el punto de venta de prueba 1; es normal que la consulta no devuelva los puntos de producción.

Datos del comprobante

Tipo de operación

operation acepta:

Valor Significado
invoice Factura
credit_note Nota de crédito
debit_note Nota de débito

letter acepta A, B o C.

El módulo realiza el mapeo a los códigos de comprobante de ARCA:

Comprobante Código ARCA
Factura A 1
Nota de débito A 2
Nota de crédito A 3
Factura B 6
Nota de débito B 7
Nota de crédito B 8
Factura C 11
Nota de débito C 12
Nota de crédito C 13

Concepto

concept acepta:

Valor Código ARCA
products 1
services 2
products_and_services 3

Para servicios, pueden enviarse opcionalmente service_from, service_to y payment_due en formato Y-m-d. Si no se envían, se utiliza la fecha del comprobante.

Cliente

customer.document_type acepta los siguientes valores:

Valor Código ARCA Descripción
cuit 80 CUIT
cuil 86 CUIL
cdi 87 CDI
le 89 Libreta de enrolamiento
lc 90 Libreta cívica
foreign_id 91 Cédula de identidad extranjera
in_process 92 Documento en trámite
birth_certificate 93 Acta de nacimiento
passport 94 Pasaporte
ci_buenos_aires 95 Cédula de identidad de Buenos Aires / RNP
dni 96 DNI
consumer_final 99 Consumidor final

Para consumidor final, el número de documento puede ser 0. El emisor se configura con su CUIT en ARCA_CUIT; el web service de facturación no se identifica mediante un CUIL.

customer.vat_condition acepta los siguientes valores:

Valor Código ARCA Descripción
responsible_registered 1 IVA responsable inscripto
exempt 4 IVA sujeto exento
consumer_final 5 Consumidor final
monotributo 6 Responsable monotributo
not_categorized 7 Sujeto no categorizado
foreign_provider 8 Proveedor del exterior
foreign_customer 9 Cliente del exterior
iva_liberated 10 IVA liberado, Ley 19.640
social_monotributo 13 Monotributista social
not_reached 15 IVA no alcanzado
promoted_monotributo 16 Monotributo trabajador independiente promovido

Importes

No se envían artículos. amounts debe contener los importes agrupados del comprobante:

Campo Significado
total Importe total
taxable_net Neto gravado
vat IVA total
exempt Importe exento
non_taxed Importe no gravado
other_taxes Otros tributos

Los seis valores son obligatorios, incluidos los que correspondan en cero. El módulo valida que:

total = taxable_net + vat + exempt + non_taxed + other_taxes

vat_rates permite discriminar una o más alícuotas:

'vat_rates' => [
    ['rate' => 21, 'taxable_base' => 100000, 'amount' => 21000],
    ['rate' => 10.5, 'taxable_base' => 50000, 'amount' => 5250],
],

Alícuotas admitidas:

Porcentaje Código ARCA
0 3
2.5 9
5 8
10.5 4
21 5
27 6

Moneda

Por defecto, el comprobante se emite en pesos:

'currency' => 'ARS',

Para dólares:

'currency' => 'USD',
'exchange_rate' => 1000,

Para euros:

'currency' => 'EUR',
'exchange_rate' => 1200,

El módulo transforma:

CRM ARCA
ARS PES, cotización 1
USD DOL, cotización obligatoria y positiva
EUR 060, cotización obligatoria y positiva

Los importes se informan en la moneda del comprobante. La cotización no convierte los importes; se envía a ARCA como MonCotiz.

Notas de crédito y débito

Las notas requieren el comprobante original asociado:

$result = app(ArcaManager::class)->authorize([
    'point_of_sale' => 1,
    'letter' => 'A',
    'operation' => 'credit_note',
    'associated_voucher' => [
        'point_of_sale' => 1,
        'letter' => 'A',
        'number' => 125,
    ],
    // customer, amounts, vat_rates y demás campos...
]);

El módulo transforma la asociación a CbtesAsoc y utiliza el tipo de factura original correspondiente.

Numeración

La numeración es independiente por punto de venta y tipo de comprobante. Por ejemplo, un punto de venta tiene contadores separados para:

Factura A
Nota de crédito A
Nota de débito A
Factura B
Nota de crédito B
Nota de débito B
Factura C
Nota de crédito C
Nota de débito C

Antes de reservar un número, el módulo consulta el último comprobante autorizado por ARCA y ajusta la secuencia local. La reserva se realiza dentro de una transacción con bloqueo para evitar números duplicados en solicitudes concurrentes.

Un número reservado no se reutiliza si una solicitud posterior falla. Esto conserva la trazabilidad y evita volver a enviar un número que pudo haber sido aceptado por ARCA durante un timeout.

Idempotencia

El CRM puede enviar idempotency_key para asociar la operación con un pedido, venta o transacción externa:

'idempotency_key' => 'crm-order-4587',

Si se repite una solicitud con la misma clave, el módulo devuelve el comprobante existente y no vuelve a llamar a ARCA.

La clave debe ser estable para la operación de negocio y no debe reutilizarse para otra factura.

Estados

Estado Significado
pending Número reservado, autorización en curso o aún no procesada
authorized ARCA asignó CAE
rejected ARCA rechazó el comprobante
failed Fallo interno antes o fuera de la comunicación final
unknown No se pudo confirmar si ARCA autorizó el comprobante

Un comprobante unknown no debe reintentarse ciegamente. Primero debe consultarse ARCA para determinar si el número fue autorizado.

Registro de operaciones

Cada intercambio con ARCA se persiste en arca_operations, incluyendo:

  • Método invocado.
  • Request enviada.
  • Response recibida.
  • Estado.
  • Correlation ID.
  • Duración.
  • Número de intento.
  • Mensaje de error.
  • Comprobante relacionado, cuando ya existe.

Las tablas principales son:

Tabla Uso
arca_points_of_sale Puntos de venta disponibles
arca_sequences Contadores por punto de venta y tipo de comprobante
arca_vouchers Comprobantes, importes, CAE y estado
arca_operations Auditoría técnica de llamadas a ARCA

El SDK se invoca con la respuesta completa para conservar también los detalles de rechazos. Las credenciales y claves privadas nunca forman parte del request registrado por este módulo.

Public API PHP

La API principal es Modules\Arca\ArcaManager:

use Modules\Arca\ArcaManager;

/** @var ArcaManager $arca */
$arca = app(ArcaManager::class);

$activePointOfSales = $arca->pointOfSales()->active();
$pointOfSale = $arca->pointOfSales()->create([
    'number' => 1,
    'name' => 'Casa central',
]);
$result = $arca->authorize($data);

También puede utilizarse la facade:

use Modules\Arca\Facades\Arca;

$result = Arca::authorize($data);

Para reemplazar el SDK durante tests o utilizar otra implementación, enlazar Modules\Arca\Contracts\ArcaClient en un service provider de la aplicación.

Testing

Los tests deben reemplazar ArcaClient por un fake. No deben utilizar credenciales reales ni realizar requests contra ARCA.

En el proyecto consumidor:

php artisan test --compact tests/Feature/Arca/ArcaManagerTest.php
vendor/bin/pint --dirty --format agent
vendor/bin/phpstan analyse modules/Arca/src --no-progress

Fuera de alcance actual

  • Emisión de comprobantes en lote.
  • Múltiples empresas emisoras.
  • PDF o representación visual del comprobante.
  • Consulta y reconciliación automática de comprobantes unknown.
  • CAEA y facturación de contingencia.
  • Pantallas frontend administrativas.