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

Mailgunwebhooks Bundle Laravel Package

azine/mailgunwebhooks-bundle

Symfony bundle that captures Mailgun webhook event data in your database, so you can search, filter, and display delivery logs beyond Mailgun's 48-hour retention, delete old events via CLI, and get alerts for blacklisted sending IPs or spam-related bounces.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit: fundamental — and disqualifying — mismatch. Despite the brief describing this as a "Laravel/PHP package," azine/mailgunwebhooks-bundle is a Symfony bundle, not a Composer package with framework-agnostic or Laravel-native wiring. There is no service provider, no publishable config, no publishable migrations, and no Blade equivalents. The integration contract is Symfony-specific end to end:

Concern What the bundle ships Laravel equivalent needed
Registration AppKernel::registerBundles() / bundles.php config/app.php providers
HTTP endpoint YAML routing resources (mailgunevent_webhook.yml, mailgunevent_admin.yml) routes/api.php, routes/web.php
Config config.yml tree with a bundle Configuration class config/*.php + config()
Persistence Doctrine ORM 3 entities + repositories Eloquent models/migrations
Templating Twig 3 Blade
CLI Symfony Console commands (mailgun:delete-events, mailgun:check-ip-in-blacklist) Artisan commands
Extension points Symfony EventDispatcher (MailgunWebhookEvent) Laravel events/listeners

The value in this repo is not the wiring — it's the domain logic: webhook signature validation, replay/deduplication, the event schema, retention pruning, and the blacklist/IP-health check. That logic is portable; the DI, routing, and ORM layers are not. A TPM should treat this as a reference implementation to be cannibalized, not a dependency to be installed.

Integration feasibility: low for drop-in, medium for reimplementation. Attempting to consume this in a Laravel app would require standing up Doctrine ORM 3 alongside Eloquent, bridging Symfony's HttpKernel/EventDispatcher, and shimming the bundle extension — a stack-of-cards that we would own forever for ~10 stars of upstream support. Realistically our own webhook receiver (controller + queued job + Eloquent mailgun_events table + signed-URL admin view) is a 1–2 engineer-week build and yields ~90% of the feature surface with zero cross-framework tax.

Technical risk: high, on multiple independent axes.

  • Maintainership: 10 stars, 0 dependents. Effectively a single-author project with no downstream ecosystem to catch regressions. Bus factor of one.
  • Runtime ceiling: 5.0.0 mandates PHP 8.5 / Symfony 7.4 / Doctrine ORM 3 / Twig 3 / Symfony Mailer. PHP 8.5 is not a runtime we can adopt broadly today; this pins our infrastructure to the author's forward-leaning upgrade cadence. If we're on Laravel, PHP 8.5 also constrains which Laravel major we can pair it with.
  • Documentation drift: The README still instructs AppKernel.php, app/config/config.yml, app/console, and Symfony 2.x router:debug, while the release notes describe Symfony 7.4 DI. That gap signals the docs are not exercised against the current code — a classic source of multi-day integration surprises.
  • Default-config leakage: Shipped defaults include [email protected] and "MuBu Properties Mailer" — a third party's production values. Any copy-paste onboarding risks sending mail attributed to an unrelated domain.
  • Security surface: This exposes a public, unauthenticated webhook endpoint plus an admin UI under /admin/. Signature validation and "safe error responses" are a 5.0.0 improvement, but a self-hosted, low-scrutiny project owning an internet-facing write endpoint is a finding in any security review we run.
  • Non-idiomatic CLI contract: -date '60 days ago' and -numberOfAttempts 5 use single-dash long options. Fine in Symfony Console; a friction point if ever ported to Artisan.

Key questions to resolve before any commitment:

  1. Is our target stack genuinely Laravel, or do we run any Symfony services where a sidecar would be cheap? This single answer flips the recommendation.
  2. Can we get to PHP 8.5 in our deployment matrix within the next two quarters, and does the Symfony 7.4 floor hold if we're not on Symfony at all?
  3. What is our actual retention requirement — is "Mailgun purges at 48h" a real business problem, or a nice-to-have? If we're on a paid Mailgun tier, the entire premise weakens.
  4. Who owns deliverability alerting today, and is there an existing observability/on-call path (PagerDuty, Slack) we should route into instead of the bundle's SMTP-to-admin approach?
  5. Do we require inbound webhook authenticity (signature), replay protection, and an audit trail as compliance obligations? If so, we need these properties regardless of build-vs-buy.
  6. Is the HetrixTools free tier (~3 blacklist checks/day) an acceptable monitoring guarantee, or do we need commercial blacklist monitoring with an SLA?

Integration Approach

Stack fit: poor as a dependency, good as a spec. The bundle assumes a full Symfony application container. In a Laravel codebase it is a foreign body. The pragmatic reading is that this repo is a well-documented requirements document for a capability we should build natively.

Migration path — three options, ranked:

Option A (recommended): Reimplement natively in Laravel. Build a small first-party module:

  • POST /webhooks/mailgun route with HMAC signature verification (Mailgun-Signature/Timestamp/Token) and a constant-time compare.
  • mailgun_events table keyed on a natural dedup key (Mailgun event ID / signature token + timestamp) so retries are idempotent.
  • Ingestion on a queue (ShouldQueue) so Mailgun's retry budget is never spent waiting on our DB.
  • Artisan commands mirroring the two CLI features: mailgun:prune --before="60 days ago" and mailgun:check-ip.
  • Admin read-only view via existing auth middleware and existing policies — no new unauthenticated /admin/ surface.
  • Alerts routed through our existing notification channel, not raw SMTP. This trades ~1–2 engineer-weeks for zero cross-framework debt, native auth/authorization, and full control of the PHP version matrix.

Option B: Sidecar Symfony service. Deploy the bundle as an isolated Symfony app that owns only the webhook receiver and event store, exposing a read API to the Laravel app. Viable if we already operate Symfony services or want strict blast-radius isolation for a public endpoint. Cost: a second runtime, a second deploy pipeline, a second security review, and a shared-database or API contract to maintain. Justified only if Option A's timeline is unattainable.

Option C (not recommended): Fork and shim. Replace Doctrine with Eloquent and Symfony wiring with a Laravel service provider. We would inherit the domain logic but also the maintenance burden of a hard fork with no upstream merge path (upstream is on Symfony 7.4/Doctrine 3 by design). Rejected: high effort, permanently orphaned.

Compatibility notes if any option carries upstream code forward:

  • PHP 8.5 is a hard floor for 5.0.0. If our fleet is on 8.2/8.3, we'd be pinned to a pre-5.0 tag — which means no signature validation, no replay/dedup, no bounded retries. That's a materially worse security posture than building Option A ourselves.
  • Doctrine ORM 3 and Eloquent can coexist in one Laravel app, but that means two migration systems and two connection configs for what is one table. Not worth it.
  • The bundle's Twig views are not portable; budget for a rebuild of the admin UI in any scenario.

Sequencing (assuming Option A):

  1. Week 0 — Confirm the premise. Verify the retention gap actually bites on our Mailgun plan; confirm we lack equivalent data in our ESP dashboard or data warehouse. Kill the project here if the answer is "we're on a paid tier with extended retention."
  2. Week 1 — Schema and receiver. Landing table, dedup key, signature verification, queued ingestion, sandbox-endpoint test with Mailgun's "Send Test" button. Ship behind a feature flag; Mailgun webhooks point at the new endpoint only after signature tests pass.
  3. Week 1–2 — Retention and health checks. Prune command and blacklist-IP command, both on the Laravel scheduler, both with failure alerting on non-zero exit.
  4. Week 2 — Read surface and alerting. Internal event list/filter view behind existing auth; bounce/spam-complaint alerts into the existing on-call channel.
  5. Week 3 — Cutover and soak. Dual-write observation window against Mailgun's dashboard to validate counts, then deprecate any manual log-scraping.

Operational Impact

Maintenance. Option A puts a small, conventional Laravel module in our own codebase — reviewed, tested, and upgraded on our cadence. Option B inherits the upstream project's release rhythm: PHP 8.5 and Symfony 7.4 arrive when the author says so, with zero dependents to absorb the breakage first. Option C inherits the worst of both. The 5.0.0 release notes are genuinely encouraging (strict signature validation, dedup, bounded observable retries), but a forward-dated release timestamp and a "readme"-level maturity signal mean we should not assume a support relationship exists. Assume no upstream support in any scenario.

Support. No vendor, no SLA, no community of size. Escalation path is "read the source." This is acceptable for a 1,500-line internal module we wrote; it is not acceptable for an internet-facing component whose failure mode is silent loss of deliverability alerts. Budget internal ownership explicitly — a named engineer, not "the platform team."

Scaling. Volumes are modest by design (the README's framing is sub-10k emails/month). The real scaling pressure is storage, not throughput: every delivery, open, click, bounce, and complaint becomes a row, and open/click events multiply fastest. Two levers:

  • Retention policy is the primary control. Define it up front (e.g. 90 days for raw events, longer for aggregates) and make the prune command non-optional in the scheduler. The bundle's own TODO list still lists "add commands to cleanup the database periodically" — meaning if we adopt, the guardrail is ours to add anyway.
  • Dedup is a cost lever, not just a correctness lever. Mailgun retries; without idempotent ingestion, retry storms inflate both storage and any open/click metrics reported to stakeholders. Also note the blacklist check consumes a free HetrixTools quota of ~3 checks/day. That's a monitoring cadence, not real-time detection — decide whether that is adequate or whether we buy commercial blacklist monitoring.

Failure modes to design for (each needs an explicit mitigation, not a hope):

Failure mode Blast radius Mitigation
Alert email to admin silently fails Deliverability incident goes unseen Route alerts through existing observability/on-call, not raw SMTP; alert on the alerter
Cron pruner stops running Disk fill → DB degradation or outage Monitor scheduler liveness; alert on table size growth
Scheduler misconfigured after deploy Silent retention breach or premature data loss Assert row-count/age bounds in a health check
Webhook endpoint flooded by scanners/forged posts Storage exhaustion, noisy metrics Signature verification (non-negotiable), rate limiting, WAF rule, reject-and-log at edge
Mailgun retry storm after partial outage Duplicate rows, inflated engagement metrics Unique dedup key at DB level, not application-level check-then-insert
HetrixTools quota or API outage Blacklist monitoring blind Treat check as best-effort; alert on consecutive failures rather than assuming "no listings"
Admin UI exposed without auth Event data (recipient addresses, subjects) leaks Never use the bundle's /admin/ prefix as-is; wrap in existing authz
Upstream stops publishing Version drift, security patches stop Accepted only under Option A, where we own the code

Ramp-up. Deceptively steep. The README interleaves Symfony 2.x-era instructions (app/console, AppKernel, app/config) with 5.0.0's Symfony 7.4 claims, so an engineer following it literally will fail before reaching the interesting parts. Anyone onboarding must first determine which era the docs describe. For a TPM this is a warning sign about the project's internal consistency, and it argues against handing this to a junior engineer as a "just install it" task. Under Option A, ramp-up is essentially zero — the code matches the rest of our application.

Bottom line for the decision log: adopt the requirements, not the dependency. The 5.0.0 hardening (signature validation, replay/dedup, bounded observable retries) is exactly the right feature set and validates the need; the Symfony-only packaging and PHP 8.5 floor make it the wrong artifact for a Laravel estate. Recommend a native Laravel build, with the bundle retained as the reference spec and the sidecar deployment held as a fallback only if the delivery timeline collapses.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel