estratos/finkok-bundle
Laravel package integrating Finkok's CFDI web services for Mexican e-invoicing. It simplifies stamping, cancellation, and invoice management with configurable clients, clean service wrappers, reusable components, and straightforward configuration for reliable SAT-compliant workflows.
CredentialsProviderInterface keeps RFCs, environments (DEMO/PROD) and CSD material in the application — env vars, secret manager, database, active tenant. This directly supports white-label invoicing, per-tenant credentials, and a single deployment serving many issuers. That is a product-architecture decision, not just a config detail.ext-soap means the extension can be dropped from base images and build pipelines. Smaller attack surface, simpler container builds, and viability on hosts where the extension is unavailable. dom + libxml (+ openssl only when cancelling with your own CSD) is a low bar.accept_reject) plus typed DTOs mean support tooling and on-call runbooks can branch on known codes instead of string-matching SOAP faults. Retries are deliberately scoped to transport errors and transient codes (205, 302, 708, 798) and are safe because Finkok deduplicates by UUID — that is a defensible default rather than an ad-hoc retry loop.isSuccess(), getIncidences(), hasErrorCode(), assertSuccess() let a PM decide per surface whether a stamping incident is a user-visible failure or a background retry. previewStampRequest() allows envelope inspection without opening a connection, which supports audit trails and pre-production sign-off.MockHttpClient, plus 4 contract tests against Finkok, means CI does not need network access or live credentials. This is a real cost saver and a prerequisite for anything resembling TDD in a fiscal integration.Adopt when:
ext-soap, or you have a policy against it.stdClass wrangling.Look elsewhere or plan extra work when:
config/bundles.php registration, Symfony DI autowiring, Symfony HttpClient. In Laravel you would write a service provider and adapter yourself. That is feasible — Laravel already consumes Symfony components — but it is real work and should be priced in. If Laravel DX is the requirement, a Laravel-specific wrapper is cheaper.docs/errores.md are in Spanish. Your team must be able to work with Spanish-language compliance material.Maturity note for the decision record: v1.0.0 is the first stable release. The dev-main era carried two breaking changes (namespace Finkok\CfdiBundle → Estratos\FinkokBundle; credentials removed from bundle YAML in favor of CredentialsProviderInterface). No published consumers were affected, but this signals the public API is young. Contract coverage is 4 live tests — meaningful but thin — so pin the version and re-run the live group on upgrade.
For executives. This is a compliance-critical integration for Mexican electronic invoicing, released as a stable 1.0. It removes the need to build and maintain a custom SOAP client, is verified against Finkok's real DEMO and PRODUCTION servers across all 11 operations, and adds no new runtime dependency (notably no ext-soap). It is multi-tenant ready — one deployment can serve many issuers with credentials held in our secret manager rather than in config. Risks to acknowledge: single PAC dependency (Finkok), Spanish-only documentation, and a young public API. Recommendation: adopt behind a version pin, with the live contract test group in the release checklist.
For engineering. The differentiating design is the transport: envelopes are generated from the real WSDL and responses are parsed by local name, so server-side namespace prefixes cannot break deserialization. That buys testability — 219 offline tests on MockHttpClient, no network in CI — and auditability, including previewStampRequest() to inspect the envelope without connecting. Responses are immutable DTOs with typed error catalogs and no exceptions on business errors, so callers choose their own policy. Reuses the app's http_client and logger when present and exposes everything through interfaces for autowiring. Non-negotiable integration rules to design around: a 201 is not a cancellation — confirm with get_sat_status() and use isCancelled() / isInProcess() rather than treating request acceptance as success; 1 MB per XML hard limit; 72-hour stamping window; five cancellation attempts per UUID before 799; CSD is optional for cancellation; DEMO has its own timings. In a Laravel codebase, plan an adapter and service provider — the bundle's Symfony registration convention does not apply there.
How can I help you explore Laravel packages today?