setono/coolrunner-bundle
Symfony bundle that integrates the CoolRunner shipping API, letting your application create shipments, print labels, and track parcel deliveries. Provides ready-made services and configuration for connecting to CoolRunner from your Symfony app.
First, verify what you're installing. The package metadata says "Use Google Analytics in your Symfony application", but setono/coolrunner-bundle is Setono's integration with CoolRunner — a Danish shipping/fulfillment API (products, orders, shipments, labels, parcel shops, rates). The description is almost certainly a copy-paste from another Setono bundle. Run composer show setono/coolrunner-bundle and read the actual README.md before wiring anything up; do not assume the documented surface matches the metadata.
Minimal path:
composer require setono/coolrunner-bundle
With Symfony Flex the bundle is auto-registered in config/bundles.php. Configure credentials via env, never in YAML:
# config/packages/setono_coolrunner.yaml
setono_coolrunner:
# exact key names depend on the bundle's Configuration.php — confirm there
username: '%env(COOLRUNNER_USERNAME)%'
password: '%env(COOLRUNNER_PASSWORD)%'
COOLRUNNER_USERNAME=
COOLRUNNER_PASSWORD=
Where to look first, in order:
bin/console config:dump-reference setono_coolrunner — the authoritative list of config keys for the installed version.src/DependencyInjection/Configuration.php and Resources/config/services.yaml — real service IDs and constructor arguments.bin/console debug:container --tag=... or debug:autowiring coolrunner — find the client interface to type-hint.tests/ directory in the repo — usually the most honest usage examples for a low-adoption bundle.First use case worth shipping: a read-only smoke test. Pull a list of products or look up parcel shops for a postcode and dump the response, so you confirm auth, base URL, and DTO shape before touching orders. Label creation and shipment booking should come second, once you trust the client.
Wrap the client in your own adapter. Treat the bundle's client as infrastructure, not as your domain API. Define a ShipmentGateway interface in your app, implement it with the bundle's client, and inject that everywhere. This is the single highest-value pattern here because a 0-star bundle can change shape between releases, and you want the blast radius to be one class.
final class CoolRunnerShipmentGateway implements ShipmentGateway
{
public function __construct(private readonly CoolRunnerClientInterface $client) {}
public function book(Order $order): TrackingInfo
{
$response = $this->client->createShipment(/* map your Order → CoolRunner payload */);
return new TrackingInfo($response->getTrackingNumber(), $response->getId());
}
}
Keep CoolRunner IDs on your own entities. Persist coolrunner_shipment_id and tracking_number on your Order/Shipment entity at booking time. Never treat a live API read as the source of truth for whether a label exists — network failures, rotated credentials, and rate limits will make your checkout history unreliable otherwise.
Book asynchronously. Label creation and shipment booking are slow and can fail transiently. Dispatch a BookShipmentMessage on Symfony Messenger after checkout, retry with backoff, and mark the order label_pending until the handler succeeds. The bundle gives you a client; the resilience is your job.
Parcel shop selection flow (the common checkout need): fetch service points by postcode/country, cache the response per postcode for minutes-to-hours, render them as a select in the checkout form, and store the chosen service_point_id on the order. Resolve the ID to a full address at booking time rather than trusting the checkout payload.
Idempotency on booking. Pass your own order reference as the idempotency/external-reference field if CoolRunner supports one. If it doesn't, guard with a lock plus a booked_at timestamp so a double-clicked "create label" cannot produce two labels.
Testing: bind a fake implementation of the bundle's client interface in config/services_test.yaml and assert on the payloads your adapter builds. For contract tests against the real API, record fixture responses from the sandbox and feed them through Symfony's MockHttpClient.
Environment parity: use the sandbox base URL in when@dev/when@test and production in when@prod, if the bundle exposes an endpoint/host config key. If it doesn't, decorate the underlying HTTP client instead of editing the bundle.
"setono/coolrunner-bundle": "x.y.z") and read the changelog before every bump. Assume 0.x semantics — minor versions can break.%env()% with Symfony Secrets in production, and never let the credentials land in config/packages/*.yaml committed to git.bin/console debug:container to get the exact service ID, then decorate it with a logging decorator that logs method, arguments, and raw response to a dedicated channel. Do this before you write booking logic — a raw request/response log is worth more than any docs here.final and/or immutable. Map to your own value objects at the adapter boundary.decorates: decorator gives you retries, logging, metrics, and a test seam without touching vendor code.json_encode-ing an array, and assert the exact JSON in tests.config:dump-reference diff → run your adapter's contract tests against sandbox. If the bundle has no changelog, diff src/ between tags. With a package this small, budgeting an afternoon for an upgrade is realistic.How can I help you explore Laravel packages today?