phpfui/constantcontact
phpfui/constantcontact is a PHP client for the Constant Contact v3 REST API, offering OAuth2 authentication, strongly typed models, and straightforward access to contacts, lists, campaigns, and reports. Easy to drop into any PHP project.
Install with Composer:
composer require phpfui/constantcontact
Before writing any code, complete the Constant Contact OAuth 2.0 registration in their developer portal so you have a client_id, client_secret, and a redirect URI. The package is a thin, generated-style wrapper around Constant Contact's v3 REST API, so the namespace hierarchy mirrors the API's endpoint structure rather than providing Laravel-native abstractions (no Contact model, no migrations, no Eloquent). Expect to spend the first hour reading vendor/phpfui/constantcontact/src/ — the class names map almost 1:1 to the API documentation, which is faster than searching for examples.
The first real use case: read-only list of contacts. Wire up a constructor with your credentials, run the authorization code flow once to obtain an access and refresh token, persist both, then instantiate the contacts endpoint class and call its get-style method with a limit parameter. If that returns a JSON-decoded payload with a contacts array, you're connected. Commit the token persistence before you write any business logic — everything else depends on it.
Centralize the client in a service provider. Bind a singleton that hydrates credentials from config/services.php and loads the current access token from your database or cache. Don't instantiate the client inline in controllers; you'll need the same object across jobs, commands, and webhook handlers.
Own the token lifecycle explicitly. Constant Contact access tokens are short-lived. Wrap the client in a small ConstantContactService that catches 401s, calls your refresh routine, persists the new token pair, and retries once. Do not let refresh logic leak into controllers — token refreshes will happen constantly once you have any cron activity.
Use queued jobs for anything that touches contacts. Bulk contact creation and list membership updates are slow and rate-limited. A SyncContactToConstantContact job that upserts by email is the right granularity. This also gives you retries and dead-lettering for free, which matters because the API will 429 under load.
Paginate with cursors, not offsets. Constant Contact v3 returns a meta.pagination.next_cursor. Write a single eachContact(callable $cb) helper that loops until the cursor is empty — you will otherwise re-implement this in five places and get it wrong in three.
Prefer bulk import endpoints for backfills. If you're migrating an existing user table, hitting the per-contact endpoint thousands of times will both be slow and burn your rate limit. The bulk import activity endpoints accept CSV and report progress asynchronously — poll the activity ID until status is COMPLETED.
Integrate at the domain-event level. Fire UserRegistered → queue a job that creates the contact and adds it to the appropriate list. Resist the urge to write to Constant Contact inside your own model observers; keeping the sync asynchronous and idempotent makes the inevitable API outage a non-event.
Handle webhooks idempotently. Constant Contact can retry webhook deliveries. Verify the signature header before doing anything, then use the event ID as a dedupe key in your cache or jobs table.
tinker and is null in a queued worker.composer.json and read the changelog before upgrading. There is no UPGRADE.md to save you.RateLimiter in front of every call. A Retry-After header appears on 429s — respect it rather than using exponential backoff blindly.unconfirmed status and won't receive campaigns. If your "signup" flow assumes an immediate send, test against a sandbox account with the same confirmation settings as production.try/catch alone will silently swallow validation failures (e.g. duplicate email, invalid list ID).Http::fake won't intercept if the package uses Guzzle directly). Register a Guzzle middleware that dumps URI and body to a dedicated constant-contact log channel. This single step will save you the most time of anything in this list.How can I help you explore Laravel packages today?