Installation Add the package via Composer:
composer require eesnaola/afip-bundle
Publish the configuration file (if needed):
php artisan vendor:publish --provider="Eesnaola\AfipBundle\AfipServiceProvider" --tag="config"
First Use Case: CAE Request
Register the service in your config/app.php under providers:
Eesnaola\AfipBundle\AfipServiceProvider::class,
Bind the AFIP client in a service provider or use the facade:
use Eesnaola\AfipBundle\Facades\Afip;
$response = Afip::cae()->request([
'cuit' => '20123456789',
'descripcion' => 'Mi producto',
'importe' => 1000.00,
]);
Configuration
Update .env with AFIP credentials (e.g., AFIP_CERT_PATH, AFIP_CERT_PASSWORD, AFIP_CUIT).
Example .env entries:
AFIP_CERT_PATH=/path/to/cert.pem
AFIP_CERT_PASSWORD=your_password
AFIP_CUIT=20123456789
AFIP_ENV=production # or 'test'
Request-Response Cycle
Use the fluent interface for AFIP services (e.g., CAE, Comprobante, WSME):
$cae = Afip::cae()
->setCuit($cuit)
->setConcept(1) // e.g., 'Mantenimiento'
->setImpTotal(1000.00)
->request();
Comprobante (Invoice) Management Generate, sign, and send invoices:
$comprobante = Afip::comprobante()
->setType('A') // Factura A
->setPtoVta(1)
->setCbteNro(1)
->setFecha(new \DateTime())
->addItem($item1, $item2)
->sign()
->send();
Web Services (WSME) For real-time validations (e.g., CUIT, IBAN):
$response = Afip::wsme()->validateCuit('20123456789');
event(new AfipResponseEvent($response));
AfipRequestJob):
dispatch(new AfipRequestJob($requestData));
return new AfipResource($response->toArray());
Certificate Handling
AFIP_CERT_PATH points to a valid PEM file (not .key or .crt).openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
openssl_error_string() may return error:0906A068:PEM routines:PEM_do_header:bad password read if the password is incorrect.Environment Mismatch
test/production logic. Override the getEnvironment() method in your service provider:
$this->app->bind('afip.environment', function() {
return env('AFIP_ENV') === 'test' ? 'test' : 'production';
});
Deprecated PHP 7.4 Features
create_function() or call_user_func_array() with dynamic arguments (use closures instead).call_user_func() with static arrays.CUIT Validation
Afip::wsme()->validateCuit() before processing:
if (!Afip::wsme()->validateCuit($cuit)) {
throw new \InvalidArgumentException("CUIT inválido");
}
Enable Logging
Add to config/afip.php:
'debug' => env('AFIP_DEBUG', false),
'log_path' => storage_path('logs/afip.log'),
Logs will include raw requests/responses for troubleshooting.
SoapClient Errors
If you see SOAP-ERROR: Encoding: object has no '...' property, ensure your request DTO matches AFIP’s WSDL schema. Use Afip::wsdl() to inspect:
dd(Afip::wsdl()->getTypes());
Custom Services
Extend the base AfipService class to add domain-specific methods:
class CustomAfipService extends \Eesnaola\AfipBundle\Services\AfipService {
public function customRequest(array $data) {
return $this->client->__soapCall('CustomMethod', [$data]);
}
}
Middleware Add AFIP-specific middleware to validate requests before processing:
public function handle($request, Closure $next) {
if ($request->afip_required && !Afip::wsme()->validateCuit($request->cuit)) {
abort(403, 'CUIT no válido');
}
return $next($request);
}
Testing Mock the AFIP client in tests:
$mock = Mockery::mock('\Eesnaola\AfipBundle\Services\AfipService');
$mock->shouldReceive('cae->request')->andReturn(['result' => 'ok']);
$this->app->instance('afip', $mock);
How can I help you explore Laravel packages today?