pixers/salesmanago-api-bundle
A PHP bundle that integrates the SalesManago marketing automation API into your application, providing ready-made service wrappers for managing contacts, events, and email marketing data without writing raw HTTP calls yourself.
Read the repo in this order before installing anything:
composer.json — this tells you the PHP constraint (likely >=5.5/^5.6 || ^7.0 era) and which underlying client it wraps (a pixers/salesmanago-api-* or similarly named HTTP client). The bundle itself is thin; the real logic lives in that dependency.DependencyInjection/Configuration.php and Resources/config/services.yml — this is where the constructor arguments are defined. Expect four to five scalars: client_id, api_key, api_secret, the API endpoint/owner, and possibly a default contact owner or company name.Service/ or Api/ directory — the public methods you will actually call (contact upsert/delete/details, email send, event tracking).Important framing: this is a Symfony bundle. In a Laravel app you do not install the bundle. You install the underlying client package and wire it yourself in a service provider, or copy the small wrapper class out of the bundle. The bundle's only real value to you is (a) the client dependency it pins, and (b) the method signatures it exposes as a reference.
Minimal first use case — upsert a contact after registration:
composer require pixers/salesmanago-api-client # check the actual name/version in the bundle's composer.json
Then in a Laravel service provider:
$this->app->singleton(SalesManagoClient::class, fn () => new SalesManagoClient(
clientId: config('services.salesmanago.client_id'),
apiKey: config('services.salesmanago.api_key'),
apiSecret: config('services.salesmanago.api_secret'),
endpoint: config('services.salesmanago.endpoint'),
owner: config('services.salesmanago.owner'),
));
First call to prove the wiring: fetch or upsert a single contact by email. If you get a well-formed response, the signature/auth path is working; everything else is field mapping.
Wrap it, never call it directly. Put a MarketingContactRepository or SalesManagoGateway interface in front of the client. The bundle's API surface is old and unlikely to change, but the package is a dead end and you will eventually replace it with a hand-rolled HTTP client or a modern SDK. An interface makes that a one-file swap.
Queue all writes. SalesManago calls are network-bound and the 2016-era client has no retry/backoff. Dispatch a job on UserRegistered, OrderPlaced, ProfileUpdated, etc. Use $tries, backoff(), and an idempotency key (email is the contact identity — upsert is naturally idempotent, which helps).
Config via config/services.php, not YAML. Mirror the bundle's config keys into Laravel config and read from env(). Keep client_id/api_key/api_secret as secrets; the API secret signs every request, so leaking it lets anyone impersonate your account.
Field mapping deserves its own class. SalesManago contacts use fixed and custom properties (email, name, phone, plus your properties array). Map your domain model to a flat array in one place — a ContactMapper — because you will change it constantly as marketing adds fields.
Backfill with an artisan command. A salesmanago:sync-contacts command using chunkById is the standard way to seed/repair. Throttle it — this API is not built for bulk and has been known to rate-limit or reject large bursts.
Handle responses explicitly. These older SalesManago clients often return HTTP 200 with an error object in the body. Pattern: check a success/error flag on the decoded payload, throw a domain exception, and let the queue retry. Do not rely on HTTP status alone.
Event tracking as a separate concern. If you push behavioral events (logins, orders, page views) for automation triggers, keep that in its own job class per event type rather than a generic sendEvent($name, $payload) — it keeps payload shapes stable and greppable.
Webhook/receiver side (if used). SalesManago callbacks are just HTTP POSTs to your Laravel route. Verify the source and make the handler idempotent; the platform will retry on non-2xx. Put the handler behind a withoutMiddleware route only if you verify signatures yourself.
composer.json with a comment explaining why. Plan an exit.services.yml, Extension::load(), and tagged services have no equivalent in Laravel. Expect to copy the wrapper class and reimplement construction — it's ~50 lines.requestTime value. If the server clock drifts (containers, CI), requests fail with opaque auth errors. Ensure NTP is enabled; check clock skew first when auth suddenly breaks.chunkById, not get() on large tables. A failed bulk sync halfway through leaves partial state — design the upsert to be re-runnable.guzzle/transport-level logging at debug level plus dumping the signed request params is the fastest route. The bundle's wrapper rarely surfaces enough context on its own.How can I help you explore Laravel packages today?