Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Myinvois Laravel Package

jiannius/myinvois

A Laravel package for integrating with Malaysia's MyInvois e-Invoicing platform, offering helpers to submit, validate, and manage electronic invoices through the LHDN MyInvois API from PHP applications.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • The package is idiomatic Laravel: service provider + Myinvois facade, config under services.myinvois.*, an artisan command, and domain events. It slots into an existing app without introducing a new architectural pattern.
  • v1.5.0 corrects the main architectural mismatch of the previous design: LHDN submission is a two-step, high-latency, externally rate-limited operation (submit → poll) that should not run inside a web request. Moving to queueSubmission() with SubmitDocuments/PollSubmission jobs is the right shape.
  • Contracts\ResolvesMyinvoisClient + Myinvois::clientFor() is the significant addition for multi-tenant deployments — per-org credentials resolved on the worker, one client per submission enforced. This is what makes the package viable for a SaaS/agency model rather than single-tenant.
  • The in-flight lock (MyinvoisInFlightException, parent refusal when latest submission is SUBMITTED/VALID) puts idempotency in the package rather than leaving it to the integrating app. That is the correct boundary and removes a common source of duplicate submissions.

Integration feasibility

  • Adoption is genuinely incremental: all new behaviour is opt-in and submitDocuments() is unchanged. The fixes ship unconditionally, so every app gets a patch-level upgrade path independent of the queue workstream.
  • Enabling queued submission requires four things outside the package: a queue connection and worker pool, a shared lock store (Redis/database/memcached — not array or file on multi-host), a scheduler entrypoint, and stable APP_KEY across deploys (job payloads are encrypted).
  • Effort is moderate but not trivial: the package change is a composer bump plus config; the real cost is queue infrastructure, worker capacity, and observability wiring.

Technical risk

  • retry_after must exceed 120s — this is a hard configuration invariant documented in the release, and getting it wrong produces duplicate job execution. The in-flight lock mitigates but does not eliminate this; lock duration (lock_seconds) must also exceed worst-case job runtime including poll backoff.
  • If lock_store is left at a per-host store, the lock is decorative under horizontal scaling — a silent, environment-dependent failure that will not show up in single-node dev/test.
  • sync_schedule only registers a schedule when configured. If an app enables queueSubmission() but never sets sync_schedule, submissions can sit in SUBMITTED indefinitely with no reconciliation. This is a coupling hazard: queueing and sweeping must be deployed together.
  • Worker-context credential resolution is a new failure surface. clientFor() works on the web request but can fail on the worker (missing org context, different config source) — a split-brain symptom that is easy to misdiagnose.
  • Explicitly not covered live on preprod: INVALID reasons (unit tests only), rejectDocument(), and payable_rounding. Rejection and rounding paths remain unverified against the sandbox, so they should not be treated as production-proven.

Key questions

  • Which queue driver and lock store exist in the target app today, and are workers multi-host?
  • What is projected document volume per org, and what LHDN rate limits apply per client id — does the new shared per-endpoint limiter match the real quota?
  • Where do per-org credentials live, and can ResolvesMyinvoisClient resolve them from a worker without request/session context?
  • Who owns dead-letter handling for jobs that exhaust retries, and what is the business SLA for a submission that never reaches LHDN?
  • Are there downstream listeners or reports relying on the parent's updated_at being bumped on document status change (now removed by design)?
  • Is there an existing pilot cohort of real invoices to exercise INVALID and rejection paths that the sandbox run did not cover?

Integration Approach

Stack fit

  • Standard Laravel stack: queue worker process, scheduler (schedule:run cron or schedule:work), cache/lock backend, and the package's config block. No new runtime, no new datastore beyond the lock store.
  • Redis is the natural pairing (queue + lock store + rate limiter coherence). Database queue is workable but payload encryption plus document-sized payloads warrants checking row size limits.
  • PHP 8.4 deprecation fix in the validator indicates the package is tracking current PHP versions — confirm the app's PHP and Laravel versions against the package's constraint before committing.

Migration path

  • Two-phase, deliberately decoupled. Phase 1: bump to v1.5.0, take the fixes, keep synchronous submitDocuments(). Phase 2: enable queueSubmission() behind a config flag, org by org.
  • Phase 1 is the low-risk, high-value step — the fixes alone (no parent updated_at churn, shared rate limiting, no crash on null failedCallback, status no longer blanked by a missing status field) remove known production bugs.
  • Phase 2 should be gated per org or per document type so a single failure does not stall the whole e-invoicing flow.

Compatibility

  • API surface is backward compatible; new config keys have defaults. The schedule is not registered unless explicitly configured, so no surprise cron jobs on upgrade.
  • Two behavioural changes to audit even on the plain synchronous path: (a) saving document status no longer bumps the parent's updated_at — anything keyed on that (reports, sync triggers, cache busting) needs checking; (b) rate limiting is now shared per endpoint and client id instead of per submission uid — real throughput to LHDN will change, potentially upward, so verify against actual LHDN quotas.
  • Encrypted job payloads bind in-flight jobs to APP_KEY; key rotation during a deploy window could invalidate queued work. Treat key rotation as a drain-first operation.

Sequencing

  1. Upgrade package, apply fixes, run regression on the existing synchronous flow.
  2. Audit for updated_at and rate-limit dependencies; adjust downstream consumers.
  3. Provision queue connection, worker pool, shared lock store; set retry_after > 120s and lock_seconds > worst-case job time.
  4. Deploy workers, then wire ResolvesMyinvoisClient for per-org credentials and validate client resolution on the worker.
  5. Enable sync_schedule — same release as queueing, not later.
  6. Wire events (SubmissionSucceeded/SubmissionFailed/DocumentStatusChanged) into alerting and audit logging.
  7. Pilot queueSubmission() on one org or document type against preprod; keep submitDocuments() as a documented fallback.

Operational Impact

Maintenance

  • The package absorbs LHDN API versioning; the team newly owns queue infrastructure, lock store sizing, scheduler health, and config invariants (retry_after, lock_seconds, queue_connection). These are now part of the runbook, not the package's problem.
  • Config surface has grown materially (queue_connection, queue, lock_store, lock_seconds, sync_schedule, sync_limit). Treat it as reviewed infrastructure config, not per-developer local preference — drift between environments is the likely source of subtle bugs.
  • Myinvois::fake() with pollResolvesTo() substantially lowers the cost of testing the queued path in CI — worth building around deliberately.

Support

  • The new events are the support backbone: alert on SubmissionFailed, surface LHDN's INVALID reasons from the event payload to the operations or finance team, and log DocumentStatusChanged as the reconciliation audit trail.
  • MyinvoisInFlightException is correct behaviour, not an outage — the runbook must say so explicitly, or on-call will treat duplicate-attempt protection as a bug.
  • The improved unauthorized_client message (intermediary not authorised for this on-behalf-of TIN on this environment) is a real support-cost reduction; it turns a credential misconfiguration into a self-describing error.

Scaling

  • Web latency is now decoupled from LHDN, which is the primary scaling win: request throughput is limited by the app, submission throughput by worker count and LHDN quotas.
  • Shared per-endpoint, per-client-id rate limiting means the ceiling is per org, enforced globally across all workers — accurate, but it will now actually throttle. Size worker concurrency against the real quota rather than against previous observed behaviour.
  • The one-client-per-submission enforcement keeps credential handling predictable under concurrency; scaling workers does not fan out auth contexts.
  • Document volume growth is absorbed by worker count and sync_limit on the sweep, not by request-path changes.

Failure modes

  • Queue backlog → submissions delayed, poll windows stretched, lock_seconds exceeded → lock expiry mid-job. The redelivery/superseded guard prevents double POST in the common case, but the window should be monitored.
  • sync_schedule unset or scheduler unhealthy → documents stuck in SUBMITTED with no status progression. Alert on submission age, not just on failures.
  • Worker-side credential resolution failing while web-side works — manifests as auth errors only on async paths; monitor worker job failure rates separately from request error rates.
  • APP_KEY rotation invalidating encrypted in-flight payloads; retry_after < 120s producing duplicate execution.
  • Retry exhaustion and dead-lettering — the package does not specify terminal handling; the app must own it or submissions silently disappear.
  • Untested surfaces remain live risks: INVALID handling, rejectDocument(), and payable_rounding are unit-tested at best. Rejection and rounding are financially material; pilot them explicitly before relying on the package for those flows.

Ramp-up

  • Opt-in design means the team can learn the queued path on one org while the business continues on the synchronous path — low blast radius.
  • Realistic ramp: 1–2 days for the upgrade and fixes regression, 1–2 weeks for queue infrastructure, worker credentials, sweep scheduling, and event-driven alerting before any production org is switched to queueSubmission().
  • Highest-value early wins are the unconditional fixes; the queueing migration should be scheduled as its own piece of work with a pilot cohort, not bundled into a routine dependency bump.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo