splash/mailchimp
A Laravel package for integrating Mailchimp with your application, providing a simple way to connect to the Mailchimp API and manage lists, subscribers, and campaigns from your PHP code.
Start by confirming the package actually installs in your target Laravel version before writing any code around it. splash/mailchimp is part of the Splash connectors family, which predates and sits outside the Laravel ecosystem proper — it is a Symfony-style bundle (MailChimpBundle) exposing a service definition plus a thin connector that wraps an embedded MailChimp client library (the "Embedded" suffix means the HTTP client is vendored in rather than pulled as a hard dependency). With zero stars and zero dependents, treat it as unvetted third-party code: audit composer.json, the Resources/config/services.* file, and the connector class before adopting it.
Minimal steps:
composer require splash/mailchimp
Resources/config/ directory — this is where the service ID, API key parameter, and default list ID are declared. That file tells you more about intended usage than any README will.subscribe($listId, $email, $mergeVars), unsubscribe(...), getLists(), getMembers(...).MailChimpServiceProvider (Laravel's container won't auto-discover a Splash bundle). Bind a singleton that receives your API key from config('services.mailchimp.key').First use case — and the one that justifies the package at all — is a newsletter signup endpoint: POST /newsletter → validate the email → dispatch a queued job → subscribe(config('services.mailchimp.list_id'), $email, ['FNAME' => $name]). If that flow cannot be made to work cleanly in an hour, stop; the package is not saving you anything over drewm/mailchimp-api or Laravel's own Http facade against API v3.
Wrap, never inject directly. The connector is the only surface you should touch, and it should be bound behind your own interface (NewsletterSubscriber). That single indirection makes the package swappable and lets you mock it in tests without depending on Splash's container semantics.
$this->app->singleton(NewsletterSubscriber::class, fn ($app) => new MailChimpSubscriber(
$app->make('splash.mailchimp.connector'),
config('services.mailchimp.list_id'),
));
Configuration and credentials. Move everything the bundle declares as a parameter into config/services.php:
'mailchimp' => [
'key' => env('MAILCHIMP_KEY'),
'list_id' => env('MAILCHIMP_LIST_ID'),
],
Bind these into the connector at container resolution time. Never let the bundle read parameters straight out of parameters.yml-style config — in Laravel that convention doesn't exist and you'll end up hardcoding.
Queue every write. Subscribes and unsubscribes are network calls to a service that rate-limits you, so they belong in ShouldQueue jobs with $tries = 3 and an exponential backoff ([10, 60, 300]). This also decouples a MailChimp outage from your signup form's response time.
Treat subscribe as idempotent. The most common day-to-day pattern is "user does X, ensure they're on list Y." Model it as an upsert: attempt the subscribe, catch the "already exists" error, and treat it as success rather than branching on a prior lookup. This halves your API calls and removes a race condition.
Batch flows. For imports or resyncs, chunk your users (500 per batch is a safe ceiling against MailChimp's batch endpoint limits) and log per-item results rather than failing the whole chunk. Record the MailChimp email_address and the resulting status so you can reconcile later.
Test at the seam. Because the embedded client likely does not use Guzzle in a way that plays nicely with Http::fake(), bind a fake NewsletterSubscriber in feature tests and unit-test your own adapter against a stubbed connector. Do not try to intercept the embedded client's transport.
drewm/mailchimp-api copy. v2 is long dead. Check whether the embedded client targets /3.0/ paths; if it doesn't, this package is a liability and you should call API v3 directly.^5 or ^6). Modern Laravel ships Guzzle 7 — expect a Composer resolution fight. Isolate in a separate package/service if you cannot resolve it.abc123-us14). The client derives the base URL from that suffix. If you see 401s on every call, verify the suffix survived your .env and wasn't truncated by a # or quote mishandling.title: "Member Exists". Catching and swallowing this is correct; surfacing it to the user is not.subscribed, pending (double opt-in), unsubscribed, cleaned, archived are distinct. A signup flow that expects subscribed will silently mis-report if the list is set to double opt-in. Read the returned status rather than assuming.FNAME to a list without that merge tag configured throws a 400 that reads unrelated to the cause. Validate your merge var keys against getLists() output once, in a test.final, subclass it and override the transport or add a retry() decorator. If it is final, decorate the connector behind your own interface from day one — that decision is cheap upfront and expensive later.How can I help you explore Laravel packages today?