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

Mailchimp Bundle Laravel Package

welp/mailchimp-bundle

Symfony bundle that integrates the MailChimp API into PHP applications, providing configuration and dependency-injection services for managing lists, subscribers, and newsletter signups. Simplifies MailChimp setup and API access within Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a compatibility check. welp/mailchimp-bundle is a Symfony bundle, not a Laravel package. It has no ServiceProvider, no facade, no auto-discovery, and no publishable Laravel config. If your goal is "call MailChimp from a Laravel app," this is the wrong dependency — it wraps drewm/mailchimp-api (the de-facto PHP V3 client), and you should depend on that directly, or on spatie/laravel-newsletter if you want a Laravel-native abstraction. Reach for this bundle only if you are actually in a Symfony app, or maintaining a Symfony service that a Laravel app talks to over HTTP.

If you are in Symfony:

  1. composer require welp/mailchimp-bundle
  2. Register Welp\MailChimpBundle\WelpMailChimpBundle in config/bundles.php
  3. Add config — see src/DependencyInjection/Configuration.php in the repo, because the README is thin. Expect something close to:
    # config/packages/welp_mail_chimp.yaml
    welp_mail_chimp:
        api_key: '%env(MAILCHIMP_API_KEY)%'
    
  4. Inject the bundle's service (Welp\MailChimpBundle\Service\...) into your own service via constructor autowiring. Prefer injecting a concrete type over $container->get() so the alias stays visible to static analysis.

Where to look first, in order: DependencyInjection/Configuration.php (the real config contract), Service/ (public API surface and method names), then Resources/config/services.xml/.yaml (service IDs and aliases). The README's examples are the least reliable artifact in a 44-star bundle; the DI definitions are the source of truth.

First use case: subscribe an address to a list. You need the list ID (a 10-char hex string, from the MailChimp UI) plus the member payload:

$mailChimp->addMemberToList($listId, strtolower($email), [
    'status'        => 'subscribed', // or 'pending' for double opt-in
    'merge_fields'  => ['FNAME' => $firstName, 'LNAME' => $lastName],
    'tags'          => ['lead'],
]);

Verify the exact method name against src/Service/ before you ship — the wrapper exposes a subset of the DrewM surface, and it is not documented.

Implementation Patterns

Wrap it, don't call it directly. The single highest-value pattern with a low-activity bundle is a one-class indirection. Define your own NewsletterClient interface, implement it with the bundle's service, and inject that everywhere. When the bundle stalls (last release Oct 2025, zero dependents), you swap one implementation for raw drewm/mailchimp-api or Laravel's Http facade without touching call sites.

interface NewsletterClient {
    public function subscribe(string $email, array $mergeFields = []): void;
    public function unsubscribe(string $email): void;
    public function tag(string $email, array $tags): void;
}

Keep the email → subscriber hash mapping in one place. V3 member endpoints identify members by md5(strtolower($email)), not by email. Centralize that:

private function hash(string $email): string {
    return md5(strtolower(trim($email)));
}

Every "get / update / delete member" call needs it, and forgetting strtolower produces silent 404s that look like "member doesn't exist" rather than "you hashed it wrong."

Push writes onto a queue. MailChimp is an external API with a hard 10-requests-per-second ceiling per account and 429s that are easy to trigger from a signup spike. Fire an event or dispatch a job on registration, and let the worker do the subscribe. In Symfony that's Messenger; in Laravel, a queued job with a thin Http/DrewM client. This decouples user-facing latency from MailChimp availability.

Use upsert (PUT), not create-then-update. The V3 member endpoint is idempotent when you PUT to /lists/{id}/members/{hash} with status_if_new. That single call handles "new subscriber" and "existing subscriber changed their name" without a round-trip read. Where the bundle exposes only addMember-style methods, prefer the ones that map to PUT; if it doesn't, this is the first place you should bypass the bundle with the underlying client.

Batch for any backfill or bulk import. A one-off "sync 20k users" via per-member calls will take hours and get rate-limited. The underlying DrewM client supports newBatch() / executeBatch() (500 operations per batch, 10 concurrent). The bundle likely does not expose batching — treat bulk work as a raw-client task and document that fork in your codebase.

Model double opt-in explicitly. status: 'pending' sends MailChimp's confirmation email; status: 'subscribed' skips it and is a compliance risk in the EU/UK. Make it a parameter on your subscribe() signature, not a hardcoded string buried in the adapter.

Webhooks belong to your app, not the bundle. MailChimp's unsubscribe, profile, cleaned, and upemail webhooks arrive as form-encoded POSTs. Handle them in your own controller/route, verify the shared secret, and keep the handler idempotent — MailChimp retries. Do not expect the bundle to route or validate these for you.

Testing. Because the bundle's service is a concrete class wrapping a live HTTP client, unit tests against it are awkward. Test your NewsletterClient implementation's payload construction by extracting a pure buildMemberPayload() method, and mock the interface everywhere else. For integration tests, point the client at a MailChimp test audience, never production — a stray DELETE is irreversible.

Gotchas and Tips

  • The Laravel trap. No extra.laravel block means composer require gives you a bundle that Laravel's container will never see. You can instantiate it manually (new MailChimp($apiKey)), but at that point you've paid for the Symfony DI wiring and gotten none of it. Just use drewm/mailchimp-api.
  • API key format is load-bearing. Keys look like abc123…-us14. The suffix is the datacenter; the underlying client parses it to build the base URL. A key with the suffix stripped produces auth failures that read like permission errors. Never trim() past the hyphen.
  • Errors may be swallowed. DrewM-style clients record failures in getLastError() / getLastResponse() rather than throwing. If the bundle's wrapper doesn't surface these, a failed subscribe looks identical to a successful one. Check the return value on every write, or wrap the call in a decorator that inspects getLastError() and throws.
  • 429 is not exceptional, it's expected. Back off with jitter on 429; do not retry immediately. Track X-Request-Id from failed responses — MailChimp support asks for it.
  • V3 is list-scoped. There is no "get all members of the account" endpoint. Any "show me everyone" feature is an N-lists loop and will be slow. Cache aggressively.
  • status vs. delete. Deleting a member removes them from the audience and from your historical stats; status: 'unsubscribed' preserves the suppression record. For GDPR erasure requests you want the delete; for "user clicked unsubscribe" you want the status change. Getting this backwards creates compliance problems either way.
  • Merge field validation is server-side. Sending FNAME to a list that doesn't define it returns a 400 with a per-field error body. Field tags are per-audience, so staging and production audiences with different field setups will diverge in behavior.
  • Config cache warmup. Bundles commonly resolve %env(...)% at container compile time. If MAILCHIMP_API_KEY isn't set during bin/console cache:warmup in CI, the build fails for a reason unrelated to your tests. Set a dummy value in test env.
  • Extension point: Symfony decoration. Rather than editing vendored code, decorate the service to add logging, retry, and metric collection:
    App\Newsletter\LoggingMailChimp:
        decorates: 'welp_mail_chimp.service'  # confirm the ID in services.xml
    
    This is also the cleanest path to adding getLastError() assertions globally.
  • Pin the version. 44 stars, 0 dependents, and a score around 2.6 mean a low bus factor. Commit to a specific tag in composer.json, watch the repo for abandonware signals, and keep the NewsletterClient boundary tight enough that vendoring the ~200 lines of wrapper logic is a 30-minute exercise if you ever need to.
  • Verify the service alias before shipping. Symfony bundles frequently define a service under a FQCN alias and a short alias, and which one is public depends on the bundle's services.yaml. Run bin/console debug:container mailchimp once and write the answer down in your team's notes — it will save the next person an hour.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console