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

hurracane/mailchimp-bundle

hurracane/mailchimp-bundle is a PHP bundle for integrating Mailchimp into your application, with configuration and service wiring for mailing list, subscriber, and API interactions. It helps reduce boilerplate when adding Mailchimp-powered email workflows to PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify before you composer require. This is a Symfony bundle, not a Laravel package — so the DI wiring, config tree, and auto-registration that make up half its surface are dead code in a Laravel app. The realistic first step is to treat the repo as reference source, not a dependency:

git clone https://gitlab.com/akpranga/hurricane/mailchimp-bundle /tmp/mcb
cat /tmp/mcb/composer.json          # note symfony/* constraints
find /tmp/mcb/src -type f            # look for the client vs. the bundle glue

First place to look: locate the actual API client class. If it accepts an API key in its constructor and uses Guzzle or a PSR-18 client with no ContainerAwareInterface, ServiceSubscriberInterface, or Symfony\Component\Config\Definition\* imports, it's portable — copy those files into app/Services/Mailchimp/ (keep the license header). If the client is entangled with Symfony services, stop and use mailchimp/marketing or drewm/mailchimp-api instead.

First use case — capture a signup into an audience:

// app/Services/Mailchimp/MailchimpClient.php (harvested or wrapped)
$client->subscribe(
    listId: config('services.mailchimp.list_id'),
    email: $request->email,
    status: 'pending', // double opt-in; do NOT default to 'subscribed'
);

Wiring it into Laravel:

  1. config/services.php → 'mailchimp' => ['key' => env('MAILCHIMP_API_KEY'), 'list_id' => env('MAILCHIMP_LIST_ID')].
  2. Bind the client interface in AppServiceProvider: $this->app->bind(MailchimpClient::class, fn () => new ...).
  3. Derive the datacenter from the key suffix (-us21) — don't add a third env var someone will forget.
  4. Add a queued job (SyncNewsletterSubscriber) rather than calling the API inline in the controller.

If the integration is worth more than a sprint, skip the harvest and go straight to the official SDK behind your own MailchimpClient interface — same amount of wiring, none of the abandonment risk.

Implementation Patterns

Facade over the extracted client so call sites stay readable and swappable:

class Mailchimp extends Facade
{
    protected static function getFacadeAccessor(): string { return 'mailchimp'; }
}
// Usage: Mailchimp::subscribe(...)

Queue every write. The upstream code is synchronous and rate-limited to 10 concurrent connections; a batch backfill on a request thread will hit 429 and poison the response.

SyncSubscriber::dispatch($user)->onQueue('marketing');
// Worker: --queue=marketing with a low --tries and exponential backoff

Upsert members idempotently. Mailchimp's PUT /lists/{id}/members/{subscriber_hash} is an upsert — use it instead of POST + existence check. The subscriber hash is md5(strtolower($email)), and getting this wrong produces duplicate contacts rather than errors.

Backfills go through /batches, not loops. Chunk 500 operations per batch, then poll GET /batches/{id} until status is finished, and parse each operation's response for per-record failures — the batch itself returns 200 even when every member in it failed.

Tag on product behavior as a separate job from the subscribe, so a Mailchimp outage during signup doesn't block a user flow that never depended on it:

Mailchimp::tagMember($email, ['trial-started', 'plan-pro']);

Test doubles via the interface — a FakeMailchimpClient bound in TestCase::setUp() lets the whole suite run without network. This is the single biggest reason to write your own interface even if you harvest the upstream client unchanged.

Consent mirroring: map your internal consent state to pending / subscribed / unsubscribed explicitly — don't let Mailchimp's default subscribed status imply consent you don't have records for.

Gotchas and Tips

  • The bundle hooks never fire in Laravel. No DependencyInjection\Extension, no Configuration tree, no auto-registration. Anything you copy is just plain classes — treat it that way.
  • Composer conflict is the gating check. A Symfony 2/3/4-era bundle pins symfony/http-kernel: ^3.4 and friends; Laravel 10/11 pulls symfony/console: ^6|^7. composer require will fail resolution. Extract the files rather than fighting the resolver, and never ship with --ignore-platform-reqs as the workaround.
  • Packagist absence. If it isn't published, you're adding a vcs repository entry pinned to a commit ref — a routine composer update on an unrelated package can pull a newer Symfony component and break this dependency's satisfiability, blocking deploys. Vendor the extracted files instead.
  • Datacenter mismatch → opaque 401. The API key's -usX suffix must match the base URL. Derive it, log it once at boot, and return a clear app-side error rather than surfacing Mailchimp's response verbatim.
  • Rate limits are concurrent, not per-second. 10 simultaneous connections; 429 includes X-Mailchimp-API-Error-Code. Honor Retry-After, add jitter, and don't retry 4xx other than 429.
  • 200 doesn't mean success. Batch responses carry per-record errors; PUT upserts can return member-level errors in the body. Always inspect the parsed body, not just the status code.
  • Webhooks are likely unverified. If the bundle exposes webhook ingestion, add signature validation yourself — otherwise unsubscribe and cleaned events are spoofable.
  • API keys in YAML. The Symfony-era pattern of config-file credentials invites leakage into logs and config:cache output. Use env-only injection and redact the key in exception handlers.
  • Extension points: extend or wrap the client class; don't try to re-implement Symfony bundle semantics (compiler passes, tagged services) in a service provider. That path costs 2–3 weeks and produces a design you'll regret.
  • Ownership is the real cost. Zero stars and zero dependents means no issue history to mine, no Stack Overflow corpus, and no upstream patches. Write a runbook, register the dependency internally, and schedule a quarterly check against Mailchimp API deprecations.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle