amplify/erp
amplify/erp is a Laravel package for building modular ERP applications, offering a foundation for core enterprise resource and workflow concerns so teams can integrate and extend business functionality within existing Laravel projects.
This is an unproven package (0 stars, 0 dependents, single release dated in the future), so treat the first hour as reconnaissance rather than a normal composer require. The description — "CSD, Fact Module Adapter module" — points at Mexican electronic invoicing (CSD = Certificado de Sello Digital, the SAT-issued sealing certificate) wrapped in a pluggable "fact module adapter". Plan accordingly: you are integrating a stamping adapter, not a generic ERP.
composer require amplify/erp
php artisan vendor:publish --provider="Amplify\Erp\ErpServiceProvider" # verify the real FQCN first
php artisan migrate
Minimal setup checklist before writing any code:
composer show amplify/erp --all and check the tag list. With one release and a 2026 date, confirm the tag actually resolves and that composer.lock pins a commit hash, not just ^1.0.src/ before reading config. Look for the module service provider and any *Adapter, *Driver, *Contract, or *Interface classes. The word "Adapter" in the description strongly implies an abstract contract with at least one concrete driver — that contract is your integration surface.config/erp.php (or similarly named) will tell you more than any README: driver selection, credential paths, environment/DNS keys, sandbox flags, storage disks.migrate in a database with existing invoicing tables.First use case to attempt: issue and stamp one invoice against the sandbox/test environment of the underlying PAC (the stamping provider), using a throwaway CSD test certificate. That single round-trip exercises config loading, credential resolution, the adapter binding, the XML/CFDI payload, the persistence layer, and error handling — the five places this kind of module usually breaks. Do not attempt production stamping until you have a stored UUID and a retrievable XML in hand.
Bind through the contract, not the concrete class. The package almost certainly exposes an interface for the "fact module" and resolves a driver from config. Inject the interface everywhere in your application code, and let config choose the implementation. This is what insulates you from the package being abandoned:
// config/erp.php
'driver' => env('ERP_DRIVER', 'csd'),
'sandbox' => env('ERP_SANDBOX', true),
// your code
public function __construct(private \Amplify\Erp\Contracts\FactAdapter $fact) {}
Treat stamping as an async, idempotent operation. Stamping is a network call to a PAC, it can time out after the invoice was accepted, and retries without an idempotency key will produce duplicate CFDI. Queue the job, record your own invoice_request_id before the call, and reconcile on retry by querying the package's status/lookup method rather than blindly re-submitting.
final class StampInvoice implements ShouldQueue
{
public function __construct(public int $invoiceId) {}
public function handle(FactAdapter $fact): void
{
$invoice = Invoice::findOrFail($this->invoiceId);
if ($invoice->uuid) return; // already stamped
$result = $fact->stamp($invoice->toFactPayload());
$invoice->update([
'uuid' => $result->uuid,
'status' => $result->status,
'xml' => $result->xml, // persist raw; PACs lose things
]);
}
}
Wrap the package in a thin application-level service. One class — InvoiceStamper or similar — is the only thing that touches the package's API. You get a single place to add logging, metrics, retries, and a single place to swap in a fork or a direct PAC SDK later. Given the maintenance signals here, assume you may need to fork.
Multi-tenant / multi-company credentials. ERP integrations are rarely single-tenant: each legal entity has its own RFC and its own CSD. Do not rely on a single global config value for the certificate path. Resolve credentials per company — via a table, a disk path convention, or a scoped container binding — and check whether the package's adapter supports a per-call credential override before you build around it. If it only reads config, you'll need one config namespace per tenant or a decorator that temporarily rebinds config.
Store the artifacts you'd hate to regenerate. Persist the stamped XML, the PDF, the UUID, the PAC's response payload, and the timestamp. Never rely on the package (or the PAC) to hand them back later — most do, for a fee or a retention window, and none do reliably in sandbox.
Test with fixtures, not the sandbox. Capture one good and two or three failing PAC responses (certificate expired, malformed RFC, duplicate UUID) as JSON/XML fixtures, bind a fake implementation of the adapter contract in your test suite, and assert on your own persistence and job behavior. Integration-test against the sandbox separately and sparingly.
composer.lock, and budget for the possibility that you maintain a fork. If you can't read the source and understand the stamping path end to end in an afternoon, don't put it in the critical path of your billing..cer/.key are DER-encoded binary and not PEM — most libraries want raw DER plus the password in the exact expected encoding. Passwords go in .env and out of config caching, and file paths must be readable by the PHP-FPM user. Set up an expiry alert the day you go live.php artisan config:cache will freeze the driver and sandbox flag. If credentials are read at boot into a singleton, rotating a certificate means config:clear + a deploy. Prefer resolving credentials lazily per invoice.Carbon::now() in a UTC container will silently produce wrong Fecha. Set the app timezone deliberately and don't let the package default it.php artisan migrate --pretend first. If the package uses generic table names (invoices, documents), you will collide with existing schema. Some modules let you override table names via config; many don't.$fillable/$casts and any repository layer. If none of these exist, keep your integration shallow.auth/tenant scoping; wrap or disable them in RouteServiceProvider if you find them exposed.How can I help you explore Laravel packages today?