carloschininin/facturax-contracts
Versioned DTO contracts for the Facturax API, with an optional Symfony bundle that registers validation and normalization services. Covers invoices, credit notes, daily summaries, voided documents, shipping guides, perceptions, retentions, and reversals, keeping payloads decoupled from Greenter.
Architecture fit
This is a contract package, not a runtime. It ships versioned DTOs (Api\V1\Dto\...) plus two services (ContractValidator, ContractNormalizer) and an optional Symfony bundle. That shape is actually a good fit for an API-integrating Laravel app: the payload vocabulary is versioned by namespace, and the public DTOs are explicitly decoupled from Greenter\Model\*, so Facturax can change its internal Greenter mapping without breaking client payloads. The domain is Peru/SUNAT e-invoicing via Greenter (comprobantes, notas, resúmenes diarios, bajas, reversiones, guías de remisión, retención/percepción) — not Facturae/CFDI/DIOT as prior write-ups imply; scope that before circulating any business case.
The boundary is also clean in one important way: authenticated emisor, SOL credentials, certificates, and SUNAT/OSE endpoints are deliberately out of the contract. That is the correct security posture and means adopting this package does not pull credential handling or network IO into your dependency graph.
Where it fits poorly: the package is DTOs and a validator, with no HTTP client, no response/error DTOs, no CDR handling, and no consulta-de-validez surface. The contract is effectively one-directional (request only). Your team still owns the response taxonomy, retry semantics, and reconciliation layer. And the Symfony bundle is meaningless in Laravel — you get the DTOs, but none of the bundle value.
Integration feasibility
Mechanically trivial: composer require, no migrations, no schema, no services to run. In Laravel you would skip the bundle entirely and bind ContractValidator/ContractNormalizer in AppServiceProvider (or just instantiate them), then map FormRequest/Job payload → DTO → validate/normalize → Laravel Http call to Facturax.
The blocker is the constraint, not the code. README states PHP 8.5+ and Symfony 8.x, while release 0.0.2 notes claim Symfony 7.1 / PHP 8.3. Both cannot be true. If the README reflects composer.json, this package will not install on any current Laravel (11/12 target PHP 8.2–8.4), and a hard php: ^8.5 floor can block composer update for your whole app. That single line is the gating question for the entire evaluation.
Technical risk
Api\V1) is the right mitigation, but only protects you between declared versions — pre-1.0 there is no promise that V1 stays put.ContractValidator do structural checks, or SUNAT catalog rules (tipo de documento, IGV rates, ubigeo, detracción percentages)? The README never says. If it is structural only, compliance sign-off still lands on you and the "validate locally before persisting" benefit is smaller than it looks.thegreenter/demo/examples" — demo parity, not a guaranteed catalog. New or edge document types will require local DTOs, and locally-forked DTOs drift from what the API accepts.symfony/validator/serializer, you inherit those and their version pins — a real conflict vector alongside Laravel's own Symfony components.Key questions
composer.json actually declare for php and symfony/* — and does 0.0.2 match the README?symfony/framework-bundle requirement)?ContractValidator enforce — shape/types only, or SUNAT catalog semantics? Are there tests proving rejection cases?Api\V1, and how will V2 be introduced?Stack fit
Good conceptual fit for Laravel: plain PHP DTOs, JSON reference fixtures in docs/examples/v1/, PHPUnit tests, no framework lock-in for the contract itself. Treat it as a shared schema artifact consumed by a Laravel client, not as a framework library. Anti-fit: the Symfony bundle (ignore it) and the PHP floor (must be resolved).
Migration path
"carloschininin/facturax-contracts": "0.0.2"), never a caret range, until upstream reaches 1.0.composer why symfony/* and composer show --tree to enumerate transitive Symfony packages before adopting.FacturaxClient + DtoMapper. Nothing in your domain should reference CarlosChininin\FacturaxContracts\Api\V1\... directly except that mapper. This makes a fork, a V2 upgrade, or a full replacement a one-file change.Compatibility
Namespace versioning lets V1 and V2 coexist — design your mapper for that now. Keep the normalized JSON as the canonical outbound document (store it for audit/replay); that decouples you from DTO class churn. Laravel side: bind the two services as singletons, validate in a FormRequest or a pre-flight job step, and use Http:: for the actual call.
Sequencing
docs/examples/v1/*.json files, hydrate DTOs, run ContractValidator, assert pass/fail. This gives you an executable definition of "supported" independent of the README.Maintenance Almost no runtime surface — no IO, no state, no migrations — so operational maintenance is near zero. Dependency maintenance is the cost: single maintainer, pre-1.0, no stated BC policy. Budget a small recurring allowance for reviewing releases and being fork-ready. Keep the fork in a private VCS so a constraint break is a one-line recovery.
Support Effectively none beyond the upstream author. Escalation path is a GitHub issue. Treat internal ownership as a given: one named engineer owns the fork, the fixture harness, and the mapper. Do not let this become unowned infrastructure.
Scaling Negligible. DTO hydration and validation are CPU-cheap and stateless, so horizontal scaling is free. The only real consideration is placement: for high-volume bursts, run validation in queue workers rather than the request path so p95 latency on the user-facing endpoint is unaffected. Autoload/opcache footprint is trivial.
Failure modes
php/symfony floors; your composer update breaks. Mitigation: fork + pin, monitor with Dependabot on a mirror.ContractNormalizer output changes shape across versions, corrupting historical comparisons. Mitigation: version-stamp every stored normalized payload.Ramp-up Half a day to competency for a PHP engineer — the package itself is trivial. The real ramp is domain knowledge: SUNAT catalogs, tipo de documento, IGV/tax codes, detracción and percepción rules, and what distinguishes a resumen diario from a comunicación de baja. Budget the onboarding time there, not on the dependency. Docs are in Spanish, which is appropriate for the LATAM team but should be noted for a distributed engineering org.
Recommendation for a TPM: conditional adopt, scoped narrowly. Use it as the payload vocabulary for a Laravel → Facturax integration, behind an anti-corruption layer, pinned and fork-ready, with server-side validation still authoritative. Do not treat a 1-star, 0-dependent, v0.0.x single-maintainer package as a platform bet, and gate the whole thing on resolving the PHP 8.5 / Symfony 8 constraint conflict.
How can I help you explore Laravel packages today?