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.
| Dependencia | Versión mínima |
|---|---|
| PHP | 8.2 |
| ext-soap | * |
| ext-openssl | * |
| ext-simplexml | * |
| Laravel (opcional) | 12.x |
composer require fluoroom/arca-sdk-phpLaravel registra el service provider automáticamente vía Package Auto-Discovery. Para publicar la configuración:
php artisan vendor:publish --tag=arca-configEl 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=trueSi 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.keyuse 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}";
}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);| 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 |
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.
| 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 |
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');
}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
}| 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) |
# 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-prodRequiere 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.
MIT