fluoroom / arca-sdk-php
PHP SDK for ARCA/AFIP SOAP web services (WSFEv1 invoicing + Padrón A13/A4/Constancia) — Laravel 12 ready
Requires
- php: ^8.2
- ext-openssl: *
- ext-simplexml: *
- ext-soap: *
Requires (Dev)
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^11.0
README
PHP SDK para los servicios web de ARCA/AFIP, orientado a Laravel 12.
Cubre los servicios de facturación electrónica (WSFEv1, 22 métodos) y consulta a padrón (Alcance 13, Alcance 4, Constancia de Inscripción), con autenticación WSAA transparente y una API tipada y amigable para el desarrollador.
Requisitos
| Dependencia | Versión mínima |
|---|---|
| PHP | 8.2 |
| ext-soap | * |
| ext-openssl | * |
| ext-simplexml | * |
| Laravel (opcional) | 12.x |
Instalación
composer require fluoroom/arca-sdk-php
Laravel registra el service provider automáticamente vía Package Auto-Discovery. Para publicar la configuración:
php artisan vendor:publish --tag=arca-config
Configuración
Variables de entorno
El SDK soporta credenciales independientes para homologación y producción. Las variables ARCA_CUIT / ARCA_CERT_PATH / ARCA_KEY_PATH actúan como fallback cuando ambos entornos comparten el mismo certificado.
# Entorno por defecto al llamar sin env() explícito ARCA_ENVIRONMENT=homologacion # o produccion # ── Homologación ─────────────────────────────────────────────── ARCA_HOMO_CUIT=30111111112 ARCA_HOMO_CERT_PATH=/ruta/homo/cert.pem ARCA_HOMO_KEY_PATH=/ruta/homo/clave.key # ── Producción ───────────────────────────────────────────────── ARCA_PROD_CUIT=30111111112 ARCA_PROD_CERT_PATH=/ruta/prod/certP.crt ARCA_PROD_KEY_PATH=/ruta/prod/claveP.key # ── Opcionales ───────────────────────────────────────────────── ARCA_CACHE_STORE= # Store de caché Laravel (vacío = default) ARCA_TIMEOUT=30 ARCA_WSDL_CACHE=true
Si solo usás un entorno, podés usar las variables genéricas de fallback:
ARCA_CUIT=30111111112 ARCA_CERT_PATH=/ruta/cert.pem ARCA_KEY_PATH=/ruta/clave.key
WSFEv1 — Facturación electrónica
Inicio rápido (Laravel)
use Arca\Sdk\Facades\Wsfe; use Arca\Sdk\Enums\Environment; use Arca\Sdk\Dto\Invoice; // Health check (sin autenticación) $status = Wsfe::ping(); echo $status->appServer; // "OK" // Consultar catálogos desde producción (datos reales) $tipos = Wsfe::env(Environment::Produccion)->getComprobanteTypes(); $ptos = Wsfe::env(Environment::Produccion)->getPuntosVenta(); // Autorizar una Factura C en homologación $last = Wsfe::env(Environment::Homologacion)->getLastAuthorized(ptoVta: 1, cbteTipo: 11); $nextNro = $last->nextNumber(); $invoice = new Invoice( concepto: 1, docTipo: 96, // DNI docNro: '12345678', cbteDesde: $nextNro, cbteHasta: $nextNro, impTotal: 1000.00, impTotConc: 0.00, impNeto: 1000.00, // Para clase C: subtotal va en impNeto impOpEx: 0.00, impTrib: 0.00, impIVA: 0.00, // Clase C no lleva IVA monId: 'PES', monCotiz: 1.0, condicionIvaReceptorId: 5, // Consumidor Final cbteFch: date('Ymd'), ); $result = Wsfe::env(Environment::Produccion)->authorizeInvoice(ptoVta: 1, cbteTipo: 11, invoice: $invoice); if ($result->isApproved()) { $detail = $result->details[0]; echo "CAE: {$detail->cae}"; echo "Vto: {$detail->caeFchVto}"; }
Uso sin Laravel
use Arca\Sdk\Enums\Environment; use Arca\Sdk\Wsaa\WsaaClient; use Arca\Sdk\Wsaa\CachedTokenProvider; use Arca\Sdk\WsfeClient; $wsaa = new WsaaClient(certPath: '/ruta/cert.pem', keyPath: '/ruta/clave.key'); $token = new CachedTokenProvider($wsaa, $cache, '/ruta/cert.pem'); $wsfe = new WsfeClient(tokenProvider: $token, cuit: '30111111112', environment: Environment::Produccion); $result = $wsfe->authorizeInvoice(ptoVta: 1, cbteTipo: 11, invoice: $invoice);
Referencia de métodos — WSFEv1
| Método PHP | SOAP | Descripción |
|---|---|---|
ping() |
FEDummy |
Health check (sin auth) |
authorizeInvoice(...) |
FECAESolicitar |
Autorizar comprobante por CAE |
authorizeInvoiceBatch(...) |
FECAESolicitar |
Autorizar lote de comprobantes |
requestCaea(periodo, orden) |
FECAEASolicitar |
Solicitar CAEA para una quincena |
getCaea(periodo, orden) |
FECAEAConsultar |
Consultar CAEA otorgado |
reportCaeaNoMovement(ptoVta, caea) |
FECAEASinMovimientoInformar |
Informar CAEA sin movimiento |
reportCaeaInvoice(...) |
FECAEARegInformativo |
Informar comprobante emitido bajo CAEA |
reportCaeaInvoiceBatch(...) |
FECAEARegInformativo |
Informar lote bajo CAEA |
getCaeaNoMovement(caea, ptoVta) |
FECAEASinMovimientoConsultar |
Consultar comprobantes sin movimiento |
getLastAuthorized(ptoVta, cbteTipo) |
FECompUltimoAutorizado |
Último número autorizado |
getMaxRecordsPerRequest() |
FECompTotXRequest |
Máximo de registros por request |
getInvoice(ptoVta, cbteTipo, cbteNro) |
FECompConsultar |
Consultar comprobante autorizado |
getComprobanteTypes() |
FEParamGetTiposCbte |
Tipos de comprobante |
getConceptoTypes() |
FEParamGetTiposConcepto |
Tipos de concepto |
getDocTypes() |
FEParamGetTiposDoc |
Tipos de documento |
getIvaTypes() |
FEParamGetTiposIva |
Alícuotas de IVA |
getCurrencies() |
FEParamGetTiposMonedas |
Monedas |
getOptionalTypes() |
FEParamGetTiposOpcional |
Tipos opcionales (RG específicos) |
getTributeTypes() |
FEParamGetTiposTributos |
Tipos de tributos |
getPuntosVenta() |
FEParamGetPtosVenta |
Puntos de venta del contribuyente |
getCotizacion(monId) |
FEParamGetCotizacion |
Cotización de referencia AFIP |
getPaises() |
FEParamGetTiposPaises |
Países |
getActividades() |
FEParamGetActividades |
Actividades del contribuyente |
getCondicionIvaReceptor(clase?) |
FEParamGetCondicionIvaReceptor |
Condiciones IVA del receptor |
Padrón — Consulta de contribuyentes
PadronClient expone tres sub-servicios bajo una misma interfaz: Alcance 13 (identificación), Alcance 4 (situación tributaria completa) y Constancia de Inscripción. Cada sub-servicio obtiene su propio ticket WSAA de forma transparente.
¿Qué servicio usar?
| Necesidad | Sub-servicio |
|---|---|
| Verificar si un CUIT existe/está activo y obtener nombre/domicilio | a13 |
| Conocer impuestos, regímenes, actividades y relaciones de un CUIT | a4 |
| Reproducir/validar la Constancia de Inscripción oficial (monotributo, categoría, caracterizaciones) | constancia |
Uso
use Arca\Sdk\PadronClient; use Arca\Sdk\Enums\Environment; use Arca\Sdk\Wsaa\WsaaClient; use Arca\Sdk\Wsaa\CachedTokenProvider; // Cada sub-servicio requiere su propio ticket WSAA (service id distinto) $mkProvider = fn(string $service) => new CachedTokenProvider( wsaaClient: new WsaaClient(certPath: '/ruta/cert.pem', keyPath: '/ruta/clave.key', service: $service), cache: $cache, certPath: '/ruta/cert.pem', service: $service, ); $padron = new PadronClient( a13Provider: $mkProvider('ws_sr_padron_a13'), a4Provider: $mkProvider('ws_sr_padron_a4'), constanciaProvider: $mkProvider('ws_sr_constancia_inscripcion'), cuit: '30111111112', environment: Environment::Produccion, ); // ── Alcance 13: identificación rápida ────────────────────────── $persona = $padron->a13->getPersona('20304050607'); echo $persona->razonSocial ?? "{$persona->apellido}, {$persona->nombre}"; echo $persona->estadoClave; // "ACTIVO" // Resolver DNI → CUIT/CUIL/CDI $ids = $padron->a13->getIdsByDocumento('30123456'); // ── Alcance 4: situación tributaria completa ─────────────────── $persona = $padron->a4->getPersona('20304050607'); foreach ($persona->impuesto as $imp) { echo "{$imp->descripcionImpuesto}: {$imp->estado}"; } // ── Constancia de Inscripción ────────────────────────────────── $constancia = $padron->constancia->getPersona('20304050607'); if ($constancia->hasError()) { echo $constancia->errorConstancia->error; } else { $general = $constancia->datosGenerales; echo $general->razonSocial ?? "{$general->apellido}, {$general->nombre}"; // Verificar si es monotributista if ($constancia->isMonotributista()) { $cat = $constancia->datosMonotributo->categoriaMonotributo[0] ?? null; echo "Categoría: {$cat?->descripcionCategoria}"; } // Ganancias simplificada (RG 4.6) if ($constancia->hasSimplifiedGananciasRegime()) { echo "Adherido a Ganancias Simplificada"; } } // Consulta en lote — hasta 250 CUITs por llamada, se pagina automáticamente $results = $padron->constancia->getPersonaBatch(['20304050607', '27123456789', '30500010459']); foreach ($results as $cuit => $r) { echo "$cuit: " . ($r->hasError() ? $r->errorConstancia->error : 'OK'); }
Manejo de errores
Los métodos de ítem único (getPersona) lanzan excepciones tipadas ante faults SOAP. Los métodos de lista (getPersonaBatch) nunca lanzan por ítem individual — los errores se expresan como errorConstancia dentro de cada PersonaConstancia devuelta.
use Arca\Sdk\Exceptions\PadronNotFoundError; use Arca\Sdk\Exceptions\PadronInactiveError; use Arca\Sdk\Exceptions\PadronAuthError; use Arca\Sdk\Exceptions\PadronException; try { $persona = $padron->a13->getPersona('00000000000'); } catch (PadronNotFoundError $e) { // CUIT inexistente } catch (PadronInactiveError $e) { // CUIT inactivo } catch (PadronAuthError $e) { // Token/sign inválido o CUIT representada no autorizado } catch (PadronException $e) { // Otro error de padrón }
Referencia de métodos — Padrón
| Sub-servicio | Método PHP | SOAP | Descripción |
|---|---|---|---|
a13 |
ping() |
dummy |
Health check |
a13 |
getPersona(cuit) |
getPersona |
Identificación + domicilios |
a13 |
getIdsByDocumento(doc) |
getIdPersonaListByDocumento |
Resolver DNI → CUIT/CUIL/CDI |
a4 |
ping() |
dummy |
Health check |
a4 |
getPersona(cuit) |
getPersona |
Situación tributaria completa |
constancia |
ping() |
dummy |
Health check |
constancia |
getPersona(cuit) |
getPersona_v2 |
Constancia de inscripción (un CUIT) |
constancia |
getPersonaBatch(cuits[]) |
getPersonaList_v2 |
Constancia en lote (≤250, paginado automático) |
Testing
# Todas las suites ./vendor/bin/phpunit # Por servicio ./vendor/bin/phpunit --testsuite wsaa ./vendor/bin/phpunit --testsuite wsfe ./vendor/bin/phpunit --testsuite padron # Producción (solo lectura, sin emisión) ./vendor/bin/phpunit --testsuite wsfe-prod
Requiere afipSec/ssl/cert.pem y afipSec/ssl/clave.key. El token de 12 horas se persiste automáticamente en afipSec/tokens/ para no consumir el cooldown de WSAA entre ejecuciones.
Licencia
MIT